package main

import (
	"log"
	"net/http"
	"net/rpc"
	"os"
	"strconv"
)

type InitReq struct {
}

type InitRes struct {
	N       int
	K       int
	Players []Player
}

type AddServerReq struct {
	Id   string
	Port string
	Ip   string
}

type AddServerRes struct {
}

type Player struct {
	Id   string
	Ip   string
	Port string
}

type Tracker struct {
	n       int
	k       int
	servers []Player
}

// call when player first joins the game
func (tracker *Tracker) GetInitInfo(req InitReq, res *InitRes) error {
	res.N = tracker.n
	res.K = tracker.k
	res.Players = tracker.servers
	return nil
}

// call when player crashes and the system is trying to regenerate the game servers
// need to check if we are allowed to do that (contact tracker whenever server is generated)
// should use mutex, only add one server at a time
func (tracker *Tracker) GenerateServer(req AddServerReq, res *AddServerRes) error {

	s := Player{
		Id:   req.Id,
		Ip:   req.Ip,
		Port: req.Port,
	}
	tracker.servers = append(tracker.servers, s)
	return nil
}

func main() {
	if len(os.Args) != 4 {
		log.Fatal("Wrong number of parameters...exiting")
	}

	port := os.Args[1]
	n, _ := strconv.Atoi(os.Args[2])
	k, _ := strconv.Atoi(os.Args[3])

	tracker := Tracker{
		n: n,
		k: k,
	}

	rpc.Register(&tracker)
	rpc.HandleHTTP()
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		return err
	}
	defer ln.Close()

	fmt.Println("tracker listening")
	t.ln = ln

	go t.accept()

	<-t.quitch

	return nil
}

func (t *Tracker) accept() {
	for {
		conn, acceptErr := t.ln.Accept()
		if acceptErr != nil {
			// do not return, keep accepting new conn
			fmt.Println("accept error: ", acceptErr)
			continue
		}
		defer conn.Close()

		buf := make([]byte, 8192)
		n, readErr := conn.Read(buf)
		if readErr != nil {
			fmt.Println("read error: ", readErr)
			return
		}

		t.msgch <- Message{
			conn:    conn,
			payload: buf[:n],
		}
	}
}

func (t *Tracker) handleMsgChan() {
	for msg := range t.msgch {
		t.handleMsg(msg)
	}
}

func (t *Tracker) handleMsg(message Message) {
	request := Req{}
	if err := json.Unmarshal(message.payload, &request); err != nil {
		fmt.Println("unmarshal error")
		return
	}

	switch request.Type {
	case "init":
		t.players = append(t.players, PlayerInfo{
			PlayerId:   string(request.Data),
			PlayerAddr: message.conn.RemoteAddr().String(),
		})

		for _, p := range t.players {
			fmt.Print(" ", p.PlayerId)
		}
		fmt.Println()

		res := InitRes{
			N:       t.n,
			K:       t.k,
			Players: t.players,
		}
		b, err := json.Marshal(res)
		if err != nil {
			panic(err)
		}
		message.conn.Write(b)

	case "regenerate server":
	default:
	}
}
